package org.news.web.utils;

import cn.hutool.core.util.RandomUtil;

import java.util.*;

public class UserBasedCollaborativeFiltering {

    // 计算余弦相似度
    public static double cosineSimilarity(UserBehaviorModel user1, UserBehaviorModel user2) {
        Map<String, NewsBehavior> behavior1 = user1.getNewsBehaviors();
        Map<String, NewsBehavior> behavior2 = user2.getNewsBehaviors();
        Set<String> subscribedChannels1 = user1.getSubscribedChannels();
        Set<String> subscribedChannels2 = user2.getSubscribedChannels();

        double dotProduct = 0.0;
        double normUser1 = 0.0;
        double normUser2 = 0.0;

        for (String newsId : behavior1.keySet()) {
            NewsBehavior behavior1ForNews = behavior1.get(newsId);
            NewsBehavior behavior2ForNews = behavior2.getOrDefault(newsId, new NewsBehavior(newsId, false, false, false, false));

            dotProduct += (behavior1ForNews.isVisited() ? 1 : 0) * (behavior2ForNews.isVisited() ? 1 : 0);
            dotProduct += (behavior1ForNews.isLiked() ? 5 : 0) * (behavior2ForNews.isLiked() ? 5 : 0) ;
            dotProduct += (behavior1ForNews.isBookmarked() ? 10 : 0) * (behavior2ForNews.isBookmarked() ? 10 : 0) ;
            dotProduct += (behavior1ForNews.isCommented() ? 20 : 0) * (behavior2ForNews.isCommented() ? 20 : 0) ;

            normUser1 = getNormUser2(normUser1, behavior1ForNews);
            normUser2 = getNormUser2(normUser2, behavior2ForNews);
        }

        // 考虑订阅信息
        dotProduct += subscribedChannels1.stream().filter(subscribedChannels2::contains).count();



        normUser1 += Math.pow(subscribedChannels1.size(), 2);
        normUser2 += Math.pow(subscribedChannels2.size(), 2);

        if (normUser1 == 0 || normUser2 == 0) {
            return 0; // 处理某个用户无行为数据的情况
        }

        return dotProduct / (Math.sqrt(normUser1) * Math.sqrt(normUser2));
    }

    private static double getNormUser2(double normUser, NewsBehavior behavior2ForNews) {
        normUser += Math.pow((behavior2ForNews.isVisited() ? 1 : 0), 2);
        normUser += Math.pow((behavior2ForNews.isLiked() ? 5 : 0), 2);
        normUser += Math.pow((behavior2ForNews.isBookmarked() ? 10 : 0), 2);
        normUser += Math.pow((behavior2ForNews.isCommented() ? 20 : 0), 2);
        return normUser;
    }

    // 生成推荐列表
    public static List<String> recommendNews(UserBehaviorModel targetUser, List<UserBehaviorModel> users, int k) {
        List<String> recommendedNews = new ArrayList<>();
        List<UserBehaviorModel> similarUsers = new ArrayList<>();

        // 找到与目标用户相似度最高的K个用户
        for (UserBehaviorModel user : users) {
            if (!user.getUserId().equals(targetUser.getUserId())) {
                double similarity = cosineSimilarity(targetUser, user);
                if (similarUsers.size() < k || similarity > cosineSimilarity(targetUser, similarUsers.get(similarUsers.size() - 1))) {
                    similarUsers.add(user);
                    similarUsers.sort((u1, u2) -> Double.compare(cosineSimilarity(targetUser, u2), cosineSimilarity(targetUser, u1)));
                    if (similarUsers.size() > k) {
                        similarUsers.remove(similarUsers.size() - 1);
                    }
                }
            }
        }

        // 生成推荐列表
        for (UserBehaviorModel user : similarUsers) {
            for (Map.Entry<String, NewsBehavior> entry : user.getNewsBehaviors().entrySet()) {
                String newsId = entry.getKey();
                NewsBehavior behavior = entry.getValue();

                // 仅推荐目标用户未浏览过的新闻
                if (!targetUser.getNewsBehaviors().containsKey(newsId) && behavior.isVisited()) {
                    recommendedNews.add(newsId);
                }
            }
        }

        /**
         * 随机返回6个 推荐内容 如果没有6个全部返回
         */
        if(recommendedNews.size() <= 6){
            return recommendedNews;
        }else{
            return RandomUtil.randomEleSet(recommendedNews, 6).stream().toList();
        }
    }
}
